using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Practices.CompositeUI.SmartParts;
using System.Text.RegularExpressions;

namespace DeblectorAddIn.WorkItems
{
    [SmartPart]
    public partial class TextView : UserControl,IFormatter2,IBreakTarget
    {
        public RichTextBox RichText { get { return richTextBox1; } }
        RootWorkItem _workItem;
        StringBuilder _sb;
        bool _buffering;
        public TextView(RootWorkItem wr)
        {
            InitializeComponent();
            wr.Services.Add<IBreakTarget>(this);
            _workItem = wr;
        }
        public void StartBufferize()
        {
            _buffering = true;
            _sb = new StringBuilder();
        }
        public void EndBufferize()
        {
            _buffering = false;
            Append(_sb.ToString());
        }
        public void Clear()
        {
            richTextBox1.Clear();
            _cursor = -1;
        }
        private bool allowProperties = false;
        private bool newLine = false;
        private int indent = 0;
        public void Write(string text)
		{
			this.ApplyIndent();
            richTextBox1.Select(richTextBox1.Text.Length, 0);
            richTextBox1.SelectionFont = new Font(richTextBox1.Font, FontStyle.Regular);
            richTextBox1.SelectionColor = Color.Black;
            Append(text);
		}
	
		public void WriteDeclaration(string text)
		{
			this.WriteBold(text);	
		}
		
		public void WriteComment(string text)
		{
			this.WriteColor(text, (int) 0x808080);	
		}
		
		public void WriteLiteral(string text)
		{
			this.WriteColor(text, (int) 0x800000);
		}
		
		public void WriteKeyword(string text)
		{
			this.WriteColor(text, (int) 0x000080);
		}
	
		public void WriteIndent()
		{
			this.indent++;
		}
				
		public void WriteLine()
		{
            Append("\n");
			this.newLine = true;
		}

		public void WriteOutdent()
		{
			this.indent--;
		}

		public void WriteReference(string text, string toolTip, Object reference)
		{
			this.ApplyIndent();
			Append(text);
		}

		public void WriteProperty(string propertyName, string propertyValue)
		{
			if (this.allowProperties)
			{
				throw new NotSupportedException();
			}
		}

		public bool AllowProperties
		{
			set 
			{ 
				this.allowProperties = value; 
			}
			
			get 
			{ 
				return this.allowProperties;
			}
		}
        private void Append(string Text)
        {
            if (_buffering)
                _sb.Append(Text);
            else
            {
                int pos = richTextBox1.SelectionStart;

                richTextBox1.Select(richTextBox1.Text.Length, 0);
                richTextBox1.SelectedText = Text;
                richTextBox1.Select(pos,0);
            }
            //richTextBox1.AppendText(Text);
        }
		private void WriteBold(string text)
		{
			this.ApplyIndent();
            richTextBox1.Select(richTextBox1.Text.Length, 0);
            richTextBox1.SelectionFont = new Font(richTextBox1.Font, FontStyle.Bold);
            richTextBox1.SelectionColor = Color.Black;
            Append(text);
		}

		private void WriteColor(string text, int color)
		{
			this.ApplyIndent();
            richTextBox1.Select(richTextBox1.Text.Length, 0);
            richTextBox1.SelectionColor = Color.FromArgb(color);
            Append(text);
		}

		private void ApplyIndent()
		{
			if (this.newLine)
			{
				for (int i = 0; i < this.indent; i++)
				{
                    Append("    ");
				}
				this.newLine = false;
			}
		}
        private void ColorLine(int line, Color bgColor,bool shor)
        {
            int start = richTextBox1.GetFirstCharIndexFromLine(line);
            if (shor)
                start += 4;
            int end = richTextBox1.GetFirstCharIndexFromLine(line + 1);
            richTextBox1.Select(start, end - start);
            richTextBox1.SelectionBackColor = bgColor;
        }
        int _cursor;
        int LineByOffset(uint offset)
        {
            string off = string.Format("L_{0:x4}", offset);
            int nStart;
            if (-1 != (nStart = richTextBox1.Text.IndexOf(off)))
            {
                int line = richTextBox1.GetLineFromCharIndex(nStart);
                return line;
            }
            return -1;
        }
        #region IDebugTarget Members
        public void RemoveBreakpointUI(string brk)
        {
            int selstart = richTextBox1.SelectionStart;
            int selLen = richTextBox1.SelectionLength;
            string[] bLine = brk.Split('+');
            int line = LineByOffset(uint.Parse(bLine[1]));
            if (-1 != line && bLine[0] == _iltarget)
            {
                ColorLine(line, richTextBox1.BackColor,false);
                if (_cursor == line)
                {
                    DebuggerWorkItem dr = _workItem.WorkItems.Get<DebuggerWorkItem>("debugger");
                    ColorLine(line, dr.OffsetIsExact ? Color.Yellow : Color.YellowGreen, true);
                }

            }
            richTextBox1.Select(richTextBox1.SelectionStart, 0);
            
        }

        public void UpdateUI()
        {
            int selstart = richTextBox1.SelectionStart;
            int selLen = richTextBox1.SelectionLength;
            if (_cursor != -1)
                ColorLine(_cursor, richTextBox1.BackColor,true);
            foreach (string s in _workItem.BreakPoints)
            { 
                string[] bLine = s.Split('+');
                int line = LineByOffset(uint.Parse(bLine[1]));
                if (-1 != line && bLine[0] == _iltarget)
                    ColorLine(line, Color.Red,false); 
            }
            
            DebuggerWorkItem dr = _workItem.WorkItems.Get<DebuggerWorkItem>("debugger");

            if (null != dr && dr.CurrentFunction == _iltarget)
            {
                int line = LineByOffset(dr.OffsetInCurrentIL);
                if (-1 != line )
                {
                    ColorLine(line, dr.OffsetIsExact ? Color.Yellow : Color.YellowGreen,true);
                    _cursor = line;
                    /*
                    Point p = richTextBox1.GetPositionFromCharIndex(richTextBox1.SelectionStart);
                    Rectangle r = richTextBox1.ClientRectangle;
                    r.Inflate(-20, -20);
                    if (!r.Contains(p))
                        richTextBox1.ScrollToCaret();
                     */
                    int n = richTextBox1.GetFirstCharIndexFromLine(line);
                    Point p = richTextBox1.GetPositionFromCharIndex(n);
                    if ( !richTextBox1.DisplayRectangle.Contains(p))
                        richTextBox1.ScrollToCaret();
                }
            }
            richTextBox1.Select(richTextBox1.SelectionStart, 0);
        }
        string _iltarget;
        public string ILTarget
        {
            get {
                int nLine = richTextBox1.GetLineFromCharIndex(richTextBox1.SelectionStart);
                
                if (null != _iltarget)
                {
                    if (nLine >= 0 && nLine < richTextBox1.Lines.Length)
                    {
                        string line = richTextBox1.Lines[nLine];
                        Regex r = new Regex("L_(?<1>.*?):");
                        Match m = r.Match(line);
                        if (m.Success)
                        {
                            int offset = int.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber);
                            return string.Format("{0}+{1}", _iltarget, offset);
                        }
                    }
                    return null;   
                }
                return null;
            }
            set { _iltarget = value; }
        }

        #endregion

        #region IFormatter Members


        public void WriteDeclaration(string value, object target)
        {
            Append(value);
        }

        #endregion
    }
}
